﻿//==============================================================================
// Math 
//------------------------------------------------------------------------------
///**
//  @file       Math.h
//  @brief      Math
//  @version    1.0
//  @date       2011/08/07
//*/
//==============================================================================

#pragma once

//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
#if defined(LNOTE_MSVC)
#include <float.h>  // for _isnan   VC++用? 
#endif
#include "Common.h"

//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
namespace LNote
{
namespace Core
{

//==============================================================================
// ■ Math モジュール
//-------------------------------------------------------------------------
///**
//  @brief      算術関係のモジュール
//
//  @par
//              ◆ 三角関数テーブルについて<br><br>
//              
//              プログラム開始時に角度の精度を決め、あらかじめ必要な値を
//              全て計算してテーブル(配列)に格納しておくことで
//              三角関数からの値の取得の速度を上げることができます。<br><br>
//              
//              このため、標準の三角関数はラジアン角(実数値)の角度を渡して値を取得しますが、
//              このモジュールの三角関数は initialize() に渡した値を1周とする整数(インデックス角度)
//              から値を取得します。<br>
//              インデックス角度を使うものは関数名に Idx が付いています。
//*/
//==============================================================================
namespace Math
{
    


	const lnFloat PI = 3.1415926535897932384626433832795f;	///< 円周率

    const lnFloat PI_OVER2 = 1.5708f;

    


	//atan2Idx( y, x );// y/x = 比
	// 結果の単位は一周(2πラジアン)の1/65536です(例：直角は16384)

	//----------------------------------------------------------------------
	///**
	//  @brief      三角関数テーブルの初期化
	//
	//  @param[in]  precision_ : 角度の精度 ( 1 ～ 65536 )
	//
	//  @retval     true  : 成功
	//  @retval     false : 失敗 ( precision_ の値が範囲外 )
	//
	//  @par
	//              このモジュールで使う三角関数 ( sinIdx() cosIdx() tanIdx() ) 
	//              のテーブルを初期化します。<br><br>
	//              
	//              precision_ に指定する値は1周の角度です。
	//              通常の度数法 ( 1周360度 ) であれば 360 を設定します。<br><br>
	//              
	//              ただし、特に大きなものを回転させようとする場合
	//              360 程度では荒さが目立ってしまう事があるため、
	//              できるだけ大きな値にすることをお勧めします。
	//              ( 360 を意識して使いたいのであれば 36000 等 )
	//*/
	//----------------------------------------------------------------------
	bool initializeTable( lnU32 precision_ );

	//----------------------------------------------------------------------
	///**
	//  @brief      sin
	//
	//  @param[in]  r_ : 角度 ( インデックス角度 )
	//*/
	//----------------------------------------------------------------------
	lnFloat sinIdx( int r_ );

	//----------------------------------------------------------------------
	///**
	//  @brief      cos
	//
	//  @param[in]  r_ : 角度 ( インデックス角度 )
	//*/
	//----------------------------------------------------------------------
	lnFloat cosIdx( int r_ );

	//----------------------------------------------------------------------
	///**
	//  @brief      tan
	//
	//  @param[in]  r_ : 角度 ( インデックス角度 )
	//*/
	//----------------------------------------------------------------------
	lnFloat tanIdx( int r_ );


	/// 角度 ( 度数法 )をラジアンに変換する
	inline float  DegToRad( float d_ )  { return d_ * 0.017453292519943295769236907684886f; }	// 0.017… は180 の逆数 * PI
	
	/// 角度 ( 度数法 )をラジアンに変換する
    inline double DegToRad( double d_ ) { return d_ * 0.017453292519943295769236907684886; }

	/// ラジアンを角度 ( 度数法 ) に変換する
	inline float  RadToDeg( float r_ )  { return r_ * 57.295779513082320876798154814105f; }		// 57.29… はPI の逆数 * 180
	
	/// ラジアンを角度 ( 度数法 ) に変換する
    inline double RadToDeg( double r_ ) { return r_ * 57.295779513082320876798154814105; }

	/// 小さい方を返す
    template < typename TYPE_ > TYPE_ min( TYPE_ a_, TYPE_ b_ ) { return ( a_ < b_ ) ? a_ : b_; } 

	/// 大きい方を返す
    template < typename TYPE_ > TYPE_ max( TYPE_ a_, TYPE_ b_ ) { return ( a_ > b_ ) ? a_ : b_; } 

	/// 値を範囲内に収める
    template < typename TYPE_ > TYPE_ limit( TYPE_ x_, TYPE_ min_, TYPE_ max_ ) { return ( x_ > max_ ) ? max_ : ( x_ < min_ ) ? min_ : x_; }

#if 1 // template だと float とかしたときに誤差がでる可能性があるため禁止
	/// v_ 以上の値をとる最小の2のべき乗数 (0を渡すと0b10(2)が返る。極限を渡すと0が返る)
	static inline unsigned int pow2Paddiing(unsigned int a)
	{
		unsigned int i = 1;
		while( a > ( i <<= 1 ) ) 
			if (!i) break;
		return i;
}
#else
	/// v_ 以上の値をとる最小の2のべき乗数
    template < typename TYPE_ > TYPE_ pow2Paddiing( TYPE_ v_ )
    {
        TYPE_ v = 1;
        while ( v < v_ ) { v *= 2; }
        return v;
    }
#endif

    inline double nan()
    {
        unsigned long nan[2]={0xffffffff, 0x7fffffff};
        return *(double*)nan;
    }


#if defined(LNOTE_MSVC)
	/// 値が NaN かを判定する
    inline bool isNaN( float v_ ) { return _isnan( v_ ) != 0; }

	/// 値が NaN かを判定する
    inline bool isNaN( double v_ ) { return _isnan( v_ ) != 0; }

#else

	/// 値が NaN かを判定する
    inline bool isNaN( float v_ ) { return isnan( v_ ) != 0; }

	/// 値が NaN かを判定する
    inline bool isNaN( double v_ ) { return isnan( v_ ) != 0; }

	/// 値が INF かを判定する
	//inline bool isINF( float v_ ) { return isinf( v_ ) != 0; }

	/// 値が INF かを判定する
	//inline bool isINF( double v_ ) { return isinf( v_ ) != 0; }

#endif


	//----------------------------------------------------------------------
	///**
	//  @brief      線形補間
	//
	//  @param[in]	p0_	: 開始値
	//  @param[in]	p1_	: 終了値
	//  @param[in]	t_	: 補間値 ( 0.0 ～ 1.0 )
	//
	//  @return		t_ における値
	//*/
	//----------------------------------------------------------------------
	inline static lnFloat lerp( lnFloat p0_, lnFloat p1_, lnFloat t_ )
	{
		return p0_ + ( ( p1_ - p0_ ) * t_ );
	}

	//----------------------------------------------------------------------
	///**
	//  @brief		等加速度運動
	//
	//	@param[in]	p_	: 開始値
	//	@param[in]	v_	: 初速度
	//	@param[in]	a_	: 加速度
	//  @param[in]	t_	: 時間
	//
	//	@return		t_ における値
	//*/
	//----------------------------------------------------------------------
    inline static lnFloat accel( lnFloat p_, lnFloat v_, lnFloat a_, lnFloat t_ )
    {
	    return p_ + ( v_ * t_ ) + ( 0.5f * a_ * t_ * t_ );
    }

	//----------------------------------------------------------------------
	///**
	//  @brief      三次補間
	//
	//  @param[in]	p0_	: 開始値
	//  @param[in]	p1_	: 終了値
	//  @param[in]	v0_	: 開始速度 ( 傾き )
	//  @param[in]	v1_	: 終了速度 ( 傾き )
	//  @param[in]	t_	: 補間値 ( 0.0 ～ 1.0 )
	//
	//  @return		t_ における値
	//*/
	//----------------------------------------------------------------------
	inline static lnFloat cubic( lnFloat p0_, lnFloat p1_, lnFloat v0_, lnFloat v1_, lnFloat t_ )
	{
		lnFloat a = 2.f * ( p0_ - p1_ ) + ( v0_ + v1_ );
		lnFloat b = 3.f * ( p1_ - p0_ ) - ( 2.f * v0_ ) - v1_;
		lnFloat r = a;
		r *= t_;
		r += b;
		r *= t_;
		r += v0_;
		r *= t_;
		return r + p0_;
	}

	//----------------------------------------------------------------------
	///**
	//  @brief      Catmull-Rom 補間
	//
	//  @param[in]	p0_	: 第 1 通過点
	//  @param[in]	p1_	: 第 2 通過点
	//  @param[in]	p2_	: 第 3 通過点
	//  @param[in]	p3_	: 第 4 通過点
	//  @param[in]	t_	: 補間値 ( 0.0 ～ 1.0 )
	//
	//  @return		t_ における値
	//
	//  @par
	//              t_ = 0.0 のときに p1_、t_ = 1.0 のときに p2_ が返ります。
	//*/
	//----------------------------------------------------------------------
	inline static lnFloat catmullRom( lnFloat p0_, lnFloat p1_, lnFloat p2_, lnFloat p3_, lnFloat t_ )
	{
		lnFloat v0 = ( p2_ - p0_ ) * 0.5f;
		lnFloat v1 = ( p3_ - p1_ ) * 0.5f;
		return ( 2.0f*p1_ - 2.0f*p2_ + v0 + v1 )*t_*t_*t_ +( -3.0f*p1_ + 3.0f*p2_ - 2.0f*v0 - v1 )*t_*t_ + v0*t_ + p1_;
	}


//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------

} // namespace Math
} // namespace Core
} // namespace LNote

//==============================================================================
//
//==============================================================================